﻿using System;
using System.Collections.Generic;
using System.Linq;
using Internal.Runtime.Services.Battle;
using Runtime.Services.Battle.Entity;
using Runtime.Services.BattleSkill;
using UnityEngine.Pool;

namespace Runtime.Services.Battle {

    /// <summary>
    /// 战斗融合数据
    /// </summary>
    public interface IDFR_ReadOnlyBattleFusionData : IReadOnlyF64FusionData, IDFR_ReadOnlyBattleData {

        /// <summary>
        /// 数据子项
        /// </summary>
        interface IItem : IDFR_ReadOnlyBattleData {

            /// <summary>
            /// 是否可忽略
            /// </summary>
            /// <param name="owner">来源数据列表</param>
            /// <param name="takedItems">已忽略数据集</param>
            bool IsIgnore(IEnumerable<IItem> owner, IReadOnlyCollection<IItem> takedItems);
        }

        new IDFR_BattleFusionData AsReadWrite();

        /// <summary>
        /// 是否可深度过滤
        /// </summary>
        bool IsDeepFilter { get; }
    }

    /// <summary>
    /// 战斗融合数据
    /// </summary>
    public interface IDFR_ValueChangedBattleFusionData : IDFR_ReadOnlyBattleFusionData, IValueChangedF64FusionData, IDFR_ValueChangedBattleData {
    }

    /// <summary>
    /// 战斗融合数据
    /// </summary>
    public interface IDFR_BattleFusionData : IDFR_ValueChangedBattleFusionData, IF64FusionData {
    }

    [Implement(typeof(IDFR_ReadOnlyBattleFusionData))]
    [Implement(typeof(IDFR_ValueChangedBattleFusionData))]
    [Implement(typeof(IDFR_BattleFusionData))]
    internal sealed class DFR_BattleFusionData : KHBD_FusionData<F64>, IDFR_BattleFusionData, IDFR_ReadOnlyBattleFusionData.IItem, IDFR_BattleDataInternal, IDFR_BattleFusionDataInternal {

        IDFR_BattleData IDFR_ReadOnlyBattleData.AsReadWrite() => throw new NotSupportedException($"{nameof(IDFR_ReadOnlyBattleData)}.{nameof(IDFR_ReadOnlyBattleData.AsReadWrite)} 已被 {nameof(IDFR_ReadOnlyBattleFusionData)}.{nameof(IDFR_ReadOnlyBattleFusionData.AsReadWrite)} 覆盖");
        IDFR_BattleFusionData IDFR_ReadOnlyBattleFusionData.AsReadWrite() {
            AssertDispose();
            return this;
        }

        protected override void Allocate() {
            base.Allocate();
            m_CachedValue = GetDefaultValue();
        }

        protected override void Release() {
            DoClear();
            m_EntityInstanceID = DFR_BattleEntityInstanceID.Empty;
            m_IsDeepFilter = false;
            m_IsRoot = false;
            base.Release();
        }

        private DFR_BattleEntityInstanceID m_EntityInstanceID;
        public DFR_BattleEntityInstanceID EntityInstanceID {
            get {
                AssertDispose();
                return m_EntityInstanceID;
            }
        }

        private bool m_IsDeepFilter;
        public bool IsDeepFilter {
            get {
                AssertDispose();
                return m_IsDeepFilter;
            }
        }

        private bool m_IsRoot;
        public bool IsRoot {
            get {
                AssertDispose();
                return m_IsRoot;
            }
        }

        void IDFR_BattleFusionDataInternal.SetIsRoot(bool isRoot) {
            AssertDispose();
            m_IsRoot = isRoot;
        }

        private IReadOnlyList<IDFR_ReadOnlyBattleFusionData.IItem> m_CachedItemList;

        public override F64 Value {
            get {
                AssertDispose();
                if (m_IsRoot) {
                    return GetValue();
                } else {
                    return base.Value;
                }
            }
        }

        private F64 m_CachedValue;

        private F64 GetValue() {
            if (IsDirty) {
                m_CachedItemList = GetItemEnumerable().ToArray();
                m_CachedValue = GetDefaultValue();
                var taskedItemSet = HashSetPool<IDFR_ReadOnlyBattleFusionData.IItem>.Get();
                foreach (var item in m_CachedItemList) {
                    if (!item.IsIgnore(m_CachedItemList, taskedItemSet)) {
                        taskedItemSet.Add(item);
                        m_CachedValue = Calculate(m_CachedValue, item.Value);
                    }
                }
                m_CachedValue = Clamp(m_CachedValue, MinValue, MaxValue);
                HashSetPool<IDFR_ReadOnlyBattleFusionData.IItem>.Release(taskedItemSet);
                ValueCached();
            }
            return m_CachedValue;
        }

        private void DoClear() {
            m_CachedItemList = null;
        }

        public override void Clear() {
            AssertDispose();
            DoClear();
            base.Clear();
        }
        public override void ClearWithOutEvent() {
            AssertDispose();
            DoClear();
            base.ClearWithOutEvent();
        }

        void IDFR_BattleDataInternal.EnableFilter(DFR_BattleEntityInstanceID entityInstanceID) {
            AssertDispose();
            m_EntityInstanceID = entityInstanceID;
        }

        void IDFR_BattleFusionDataInternal.EnableFilter(bool isDeepFilter) {
            AssertDispose();
            m_IsDeepFilter = isDeepFilter;
        }

        protected override F64 GetDefaultValue() => default;

        protected override F64 Calculate(F64 current, F64 next) {
            if (F64.MaxValue == current || F64.MaxValue == next) {
                return F64.MaxValue;
            } else if (F64.MinValue == current || F64.MinValue == next) {
                return F64.MinValue;
            } else {
                return current + next; // TODO: 需要处理数字越界
            }
        }

        protected override F64 GetDefaultMaxValue() => F64.MaxValue;

        protected override F64 GetDefaultMinValue() => F64.MinValue;

        protected override F64 Clamp(F64 value, F64 min, F64 max) => F64.Clamp(value, min, max);

        bool IDFR_ReadOnlyBattleFusionData.IItem.IsIgnore(IEnumerable<IDFR_ReadOnlyBattleFusionData.IItem> owner, IReadOnlyCollection<IDFR_ReadOnlyBattleFusionData.IItem> takedItems) {
            AssertDispose();
            return BattleSkillServiceBase.Instance.AsEffectFilter().IsIgnore(owner, this, takedItems);
        }

        private IEnumerable<IDFR_ReadOnlyBattleFusionData.IItem> GetItemEnumerable() {
            if (Items == null) {
                yield break;
            }
            foreach (var source in Items) {
                if (source is IDFR_ReadOnlyBattleFusionData.IItem item) {
                    if (source is DFR_BattleFusionData fusionData && fusionData.IsDeepFilter) {
                        var enumerable = fusionData.IsDirty ? fusionData.GetItemEnumerable() : fusionData.m_CachedItemList;
                        if (enumerable != null) {
                            foreach (var fusionItem in enumerable) {
                                yield return fusionItem;
                            }
                        }
                    } else {
                        yield return item;
                    }
                }
            }
        }
    }
}

namespace Internal.Runtime.Services.Battle {

    public interface IDFR_BattleFusionDataInternal {

        void EnableFilter(bool isDeepFilter);

        void SetIsRoot(bool isRoot);
    }
}